// synchronize to period T
1::second => dur T;
T - (now % T) => now;

1 => int bright;
0 => int color;

ConsoleInput in;
// syncing variables
dur tempT;
int tempBright;
int tempColor;
0 => int newTimeFlag;

// define scales
[0, 2, 4, 5, 7, 9, 11] @=> int maj[];
[0, 2, 3, 5, 7, 8, 10] @=> int min[];
[0, 3, 5, 7, 10] @=> int pent[];
[0, 2, 4, 7, 9] @=> int pleasant[];

[ maj, min, pent, pleasant ] @=> int scales[][];

[ 2, 4, 6 ] @=> int interval[];

[ 0, 3, 1, 3 ] @=> int meas1[];
[ 3, 0, 1, 3 ] @=> int meas2[];
//[ 0, 4, 5, 0 ] @=> int meas3[];

[ meas1, meas2] @=> int measures[][];

// define instruments
SinOsc bass => dac;
.1 => bass.gain;

SinOsc s => JCRev rev_s => dac;  // voice
.07 => s.gain;
.25 => rev_s.mix;

SinOsc v => JCRev rev_v => dac;  // voice2
.07 => v.gain;
.25 => rev_v.mix;

Bowed a => dac;
Bowed b => dac;
.35 => a.gain;
.3 => b.gain;
//.25 => rev_chord.mix;
//.5 => chord.gain;


fun void genBass(int key, float bars, int note) {
  Std.mtof(key) => bass.freq;
  for (0 => int i; i < note * bars; i++) {
    Std.rand2f(.3, .4) => bass.gain;
    (1.0/note)::T => now;
  }
}

fun void genBackground(int key, float bars) {
  // play V + I
  Std.mtof(key) => a.freq;
  Std.mtof(key+7) => b.freq;
  0.7 => a.noteOn;
  0.7 => b.noteOn;
  bars::T=>now;
  0.3 => a.noteOff;
  0.3 => b.noteOff;
}

// key: 60 = middle C, +12 per octave
// note: 4 = quarter note, 8 = eighth note
fun void genVoice(int key, float bars, int note, int set[]) {
  for (0 => int i; i < note * bars; i++) {
    Std.rand2f(.06, .09) => s.gain;
    0 => s.phase;
        
    Std.rand2f(.06, .09) => v.gain;
    0 => v.phase;
        
    Std.rand2(0,3)*12 => int octave;
    //  base key +      note           +     octave
        
    for (0 => int j; j < measures.cap(); j++) {
      for (0 => int i; i < measures[j].cap(); i++) {
        Std.mtof(key + set[measures[j][i]] + octave) => s.freq;
        findInterval(key + set[measures[j][i]] + octave, set) => v.freq;
        //if (Std.randf() > 0.5)
        //  2*(1.0/note)::T => now;
        //else
          (1.0/note)::T => now;
      }
    }
                
  }
}


fun float findInterval(int noteIndex, int set[]) {
  interval[Std.rand2(0,interval.cap()-1)] => int chosen;
  noteIndex+chosen => int currentIndex;
  1 => int multiplier;
  while (currentIndex > set.cap()-1) {
    multiplier + 1 => multiplier;
    currentIndex - set.cap() + 1 => currentIndex;
  }
  return Std.mtof(set[currentIndex]+60);
}

fun int[] setSignature(int in) {
  if (in == 0) return min;
  else if (in == 1) return pent;
  else return maj;
}


SndBuf kick => dac;
"data/kick.wav" => kick.read;
0 => kick.gain;

SndBuf hh => dac;
"data/hihat.wav" => hh.read;
0 => hh.gain;

SndBuf hhopen => dac;
"data/hihat-open.wav" => hhopen.read;
0 => hhopen.gain;

SndBuf snare => dac;
"data/snare-hop.wav" => snare.read;
0 => snare.gain;

fun void play_kick() {
  .3 => kick.gain;

  0 => kick.pos;
  1::T => now;
}

fun void play_hh() {
  .2 => hhopen.gain;
  .2 => hh.gain;

  Std.randf() => float r;
  //  if (r < -0) {
  0 => hhopen.pos;
  .25::T => now;
  0 => hh.pos;
  .25::T => now;
  /*
    } else if (r < 0.5) {
    0 => hhopen.pos;
    .25::T => now;
    0 => hh.pos;
    .125::T => now;
    0 => hh.pos;
    .125::T => now;
    } else {
    0 => hhopen.pos;
    .125::T => now;
    0 => hh.pos;
    .0625::T => now;
    0 => hh.pos;
    .0625::T => now;
    0 => hh.pos;
    .125::T => now;
    0 => hh.pos;
    .125::T => now;
    }
    // */
}

fun void play_snare() {
  .4 => snare.gain;
  0 => snare.pos;
}


fun void read_int() {
  while(true) {
    in.prompt("") => now;
    while (in.more()) {
      StringTokenizer tok;

      in.getLine() => string s;
      tok.set(s);
      tok.next() => string arg1;
      tok.next() => string arg2;
      tok.next() => string arg3;

      Std.atof(arg1) => float period;
      period::second => tempT;
      Std.atoi(arg2) => tempBright; // 0-2
      Std.atoi(arg3) => tempColor;  // 0-11

      // set the flag for sync_time
      1 => newTimeFlag;

      /*
        if (hz > 0) {// below a certain level doesn't affect
        (1.0/hz) => float period;
        //if (period < 1.0)  // slow down to "half time" if too fast
        //  period * Math.ceil(1/period) => period;
        //else if (period > 2.0) // speed up if too long
        //  period / Math.ceil(period/2) => period;

        // set the flag for sync_time
        period::second => tempT; // half-time
        1 => newTimeFlag;
        }
      */
    }
  }
}

fun void sync_time() {
  // sync time
  if (newTimeFlag) {
    0 => newTimeFlag;
    <<< tempT >>>;
    tempT => T;
    tempBright => bright;
    tempColor => color;
  }
}


spork ~ read_int();
while (true) {
  sync_time();

  spork ~ genBass(36+color, 0.5, 8);
  spork ~ genVoice(60+color, 0.5, 8, setSignature(bright));
  spork ~ genBackground(60+color, 0.5);

  spork ~ play_kick();
  spork ~ play_hh();
  .5::T => now;

  sync_time();

  spork ~ genBass(36+color, 0.5, 8);
  spork ~ genVoice(60+color, 0.5, 8, setSignature(bright));
  spork ~ genBackground(60+color, 0.5);

  spork ~ play_snare();
  spork ~ play_hh();
  .5::T => now;
 }
